﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml;

namespace TheCopticProject.CopticCalendar
{
    public static class CopticCalendarEvents
    {
        #region Reading Events from Resources
        public enum Language { en, ar }

        public struct EventType
        {
            public int Id;
            public int Importance;
            public Dictionary<Language, string> Title { get { return _title; } }

            private Dictionary<Language, string> _title;

            public EventType(int id, int importance)
            {
                _title = new Dictionary<Language, string>();

                Id = id;
                Importance = importance;
            }
        }

        public struct FixedDateEvent
        {
            public int Month;
            public int Day;
            public int TypeId;
            public Dictionary<Language, string> Title { get { return _title; } }

            private Dictionary<Language, string> _title;

            public FixedDateEvent(int month, int day, int typeId)
            {
                _title = new Dictionary<Language, string>();

                Month = month;
                Day = day;
                TypeId = typeId;
            }

            public Date GetDateForYear(int year)
            {
                return new Date(year, Month, Day);
            }
        }

        public struct MovingDateEvent
        {
            public int DaysFromEaster;
            public int TypeId;
            public Dictionary<Language, string> Title { get { return _title; } }

            private Dictionary<Language, string> _title;

            public MovingDateEvent(int daysFromEaster, int typeId)
            {
                _title = new Dictionary<Language, string>();

                DaysFromEaster = daysFromEaster;
                TypeId = typeId;
            }

            public Date GetDateForYear(int year)
            {
                Date date = Date.GetEasterDate(year);
                if (DaysFromEaster > 0)
                    return date.AddDays(DaysFromEaster);
                else if (DaysFromEaster < 0)
                    return date.SubtractDays(-DaysFromEaster);
                else
                    return date;
            }

            public Date[] GenerateDatesForYears(int startingYear, int yearsCount)
            {
                Date[] dates = new Date[yearsCount];

                for (int i = 0; i < yearsCount; i++)
                    dates[i] = GetDateForYear(startingYear + i);

                return dates;
            }
        }

        private const string EventsResourceXmlFile = "TheCopticProject.CopticCalendar.CopticCalendarEvents.xml";

        private static Dictionary<int, EventType> _types = new Dictionary<int, EventType>();
        private static FixedDateEvent[] _fixedEvents;
        private static MovingDateEvent[] _movingEvents;

        public static Dictionary<int, EventType> EventTypes { get { return _types; } }
        public static FixedDateEvent[] FixedEvents { get { return _fixedEvents; } }
        public static MovingDateEvent[] MovingEvents { get { return _movingEvents; } }

        static CopticCalendarEvents()
        {
            XmlTextReader reader = new XmlTextReader(Assembly.GetAssembly(typeof(CopticCalendarEvents)).GetManifestResourceStream(EventsResourceXmlFile));

            if (!FindNextElement(reader, "copticCalendar"))
                throw new InvalidOperationException("Error reading events resource file");

            if (!FindNextElement(reader, "eventTypes"))
                throw new InvalidOperationException("Error reading events resource file");

            List<EventType> eventTypes = new List<EventType>();

            while (FindNextElement(reader, "eventType"))
            {
                int importance = int.Parse(reader.GetAttribute("importance"));
                int id = int.Parse(reader.GetAttribute("typeId"));
                EventType eventType = new EventType(id, importance);

                while (FindNextElement(reader, "title"))
                {
                    try
                    {
                        Language language = (Language)Enum.Parse(typeof(Language), reader.GetAttribute("language"));
                        eventType.Title[language] = reader.GetAttribute("value");
                    }
                    catch
                    {
                    }
                }

                _types[id] = eventType;
            }

            if (!FindNextElement(reader, "copticCalendarEvents"))
                throw new InvalidOperationException("Error reading events resource file");

            List<FixedDateEvent> fixedEvents = new List<FixedDateEvent>();

            while (FindNextElement(reader, "fixedDateEvent"))
            {
                int typeId;
                if (!int.TryParse(reader.GetAttribute("typeId"), out typeId))
                    typeId = -1;

                int month = int.Parse(reader.GetAttribute("month"));
                int day = int.Parse(reader.GetAttribute("day"));
                FixedDateEvent fixedEvent = new FixedDateEvent(month, day, typeId);

                while (FindNextElement(reader, "title"))
                {
                    try
                    {
                        Language language = (Language)Enum.Parse(typeof(Language), reader.GetAttribute("language"));
                        fixedEvent.Title[language] = reader.GetAttribute("value");
                    }
                    catch
                    {
                    }
                }

                fixedEvents.Add(fixedEvent);
            }

            if (reader.Name != "movingDateEvent")
                throw new InvalidOperationException("Error reading events resource file");

            List<MovingDateEvent> movingEvents = new List<MovingDateEvent>();

            do
            {
                int typeId;
                if (!int.TryParse(reader.GetAttribute("typeId"), out typeId))
                    typeId = -1;
                int daysFromEaster = int.Parse(reader.GetAttribute("daysFromEaster"));
                MovingDateEvent movingEvent = new MovingDateEvent(daysFromEaster, typeId);

                while (FindNextElement(reader, "title"))
                {
                    try
                    {
                        Language language = (Language)Enum.Parse(typeof(Language), reader.GetAttribute("language"));
                        movingEvent.Title[language] = reader.GetAttribute("value");
                    }
                    catch
                    {
                    }
                }

                movingEvents.Add(movingEvent);
            }
            while (FindNextElement(reader, "movingDateEvent"));

            reader.Close();

            _fixedEvents = fixedEvents.ToArray();
            _movingEvents = movingEvents.ToArray();
        }

        private static bool FindNextElement(XmlTextReader reader, string name)
        {
            while (true)
            {
                if (!reader.Read())
                    throw new InvalidOperationException("Error reading events resource file");

                if (reader.NodeType == XmlNodeType.EndElement)
                    return false;

                if (reader.NodeType == XmlNodeType.Element)
                    return reader.Name == name;
            }
        }
        #endregion


    }
}
